package karma.pool.pool.proxy;

import karma.pool.pool.PoolEntry;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * A Runnable that is scheduled in the future to report leaks.  The ScheduledFuture is
 * cancelled if the proxyConnection is closed before the leak time expires.
 */
@Slf4j
public class ProxyLeakReportRunnable implements Runnable {
   static final ProxyLeakReportRunnable NO_LEAK;

   static {
      NO_LEAK = new ProxyLeakReportRunnable() {
         @Override
         void scheduleAt(ScheduledExecutorService scheduledExecutorService, long leakDetectionThreshold) {
         }

         @Override
         public void run() {
         }

         @Override
         public void cancel() {
         }
      };
   }

   private ScheduledFuture<?> scheduledFuture;
   private String connectionName;
   private Exception exception;
   private String threadName;
   private boolean isLeaked;

   ProxyLeakReportRunnable(final PoolEntry poolEntry) {
      this.exception = new Exception(" proxyConnection leak detected");
      this.threadName = Thread.currentThread().getName();
      this.connectionName = poolEntry.connection.toString();
   }

   private ProxyLeakReportRunnable() {
   }

   void scheduleAt(ScheduledExecutorService scheduledExecutorService, long leakDetectionThreshold) {
      scheduledFuture = scheduledExecutorService.schedule(this, leakDetectionThreshold, TimeUnit.MILLISECONDS);
   }

   @Override
   public void run() {
      isLeaked = true;

      final StackTraceElement[] exceptionStackTrace = exception.getStackTrace();
      final StackTraceElement[] exceptionStackTraceCopy = new StackTraceElement[exceptionStackTrace.length - 5];
      System.arraycopy(exceptionStackTrace, 5, exceptionStackTraceCopy, 0, exceptionStackTraceCopy.length);
      exception.setStackTrace(exceptionStackTraceCopy);
      log.warn("Connection leak detection triggered for {} on thread {}, stack trace follows", connectionName, threadName, exception);
   }

   public void cancel() {
      scheduledFuture.cancel(false);
      if (isLeaked) {
         log.info("Previously reported leaked proxyConnection {} on thread {} was returned to the pool (unleaked)", connectionName, threadName);
      }
   }
}
